%module "Math::Macopt";
%{
	extern "C"
	{
		#include <stdio.h>
		#include <stdlib.h>
	
		#include <sys/types.h>
		#include <time.h>
	
		#include <EXTERN.h>
		#include <perl.h>
	}
	#include "macopt.h"
	#include "base.h"
%}

/* Support double point input */
%typemap(in) double* (double* doubleList) {

	AV* list;
	int i, len=0;
	
	if (!SvROK($input)) {
	    croak("Argument $argnum is not a reference.");
	}
	if (SvTYPE(SvRV($input)) != SVt_PVAV) {
	    croak("Argument $argnum is not an array.");
	}

	// Create a temp list for storing the value
	list = (AV*) SvRV($input);
	len = av_len(list) + 1;
	doubleList = (double*) malloc(sizeof(double)*(len+1));
	
	for(i=0; i<len; i++) {
		SV** tmp = av_fetch(list, i, 0);
		// Should be both "SvIOK" and "SvNOK" to check the double value
		if (!SvNOK(*tmp) && !SvIOK(*tmp)) {
			croak("Argument $argnum contains non-double elements");
		}
		doubleList[i] = SvNV(*tmp);
	}
	doubleList[len] = NULL;

	// TODO: Important, macopt uses x[1]..x[n]
	$1 = doubleList - 1;
}

%typemap(argout) double* {

	AV* list;
	int i, len=0;

	/* Here we assume the function does not modify the argnum */
	list = (AV*) SvRV($input);
	len = av_len(list) + 1;
	av_clear(list);
	
	for(i=1; i<=len; i++) {
		SV* temp = newSVnv($1[i]);
		av_push(list, temp);
	}

// TODO: Check whether it is done by macopt already
// free($1);
}

/* Support perl callback */
%typemap(in) SV* {
	$1 = (SV*) $input;
}

/* Prototype the "Base" class */
class Base 
{
public:
	Base(
		int		 _n, 
		int		_verbose=0, 
		double 	_tolerance=0.001, 
		int 	_itmax=100, 
		int 	_rich = 1
	);

	void macoptII(double*, int);
	void maccheckgrad(double*, int, double, int);
	int size();

	void setFunc(SV*); 
	void setDfunc(SV*);
};

/* Resolve overloading functions */
%feature("compactdefaultargs") Base::Base;

/* Version number */
#define VERSION 0.02

/*
%ignore Base::func(double*);
%ignore Base::dfunc(double*, double*);

%include "macopt.h"
%include "base.h"
*/
